//
//  EventTracking.swift
//  EventTracking
//
//  Created by 孙长坦 on 2022/11/28.
//

import Foundation
import UIKit
import Logging
import RxSwift
import LibNetwork


public class EventTracking: NSObject, EventTrackingProtocol {
    private static let maxUploadLogsCount: Int = 10
    
    var logger = Logger(label: "EventTracking")
    
    private let eventTrackingQueue = DispatchQueue(label: "eventTrackingQueue", qos: DispatchQoS.background)
    
    private let eventTrackingSchedule: SerialDispatchQueueScheduler
    
    private var disposable: Disposable?
    
    private var logs: [[String: Any]]
    
    private var uploadingLogsCount = 0
    
    
    override init() {
        eventTrackingSchedule = SerialDispatchQueueScheduler(queue: eventTrackingQueue, internalSerialQueueName: "eventTrackingSchedule")
        
        if let logsData = ServiceManager.shared.cacheService.globalCache.object(type: Data.self, key: "logsData"),
           let logs = try? JSONSerialization.jsonObject(with: logsData) as? [[String: Any]] {
            self.logs = logs
        } else {
            logs = []
        }
        
        uploadingLogsCount = ServiceManager.shared.cacheService.globalCache.int(key: "uploadingLogsCount") ?? 0
        
        super.init()
    }
    
    public func initEventTracking() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(didFinishLaunchingNotification(_:)),
            name: UIApplication.didFinishLaunchingNotification,
            object: nil)
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(applicationWillEnterForeground(_:)),
            name: UIApplication.willEnterForegroundNotification,
            object: nil)
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(applicationDidEnterBackground(_:)),
            name: UIApplication.didEnterBackgroundNotification,
            object: nil)
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(applicationWillTerminate(_:)),
            name: UIApplication.willTerminateNotification,
            object: nil)
    }
    
    @objc func didFinishLaunchingNotification(_ notification: NSNotification) {
        tracking(event: "appCreate", params: nil)
    }
    
    @objc func applicationWillEnterForeground(_ notification: NSNotification) {
        tracking(event: "appStart", params: nil)
    }
    
    @objc func applicationDidEnterBackground(_ notification: NSNotification) {
        tracking(event: "appStop", params: nil)
    }
    
    @objc func applicationWillTerminate(_ notification: NSNotification) {
        tracking(event: "appDestroy", params: nil)
    }
    
    public func tracking(event: String, params: [String: Any]?) {
        var data: [String: Any] = [
            "event": event,
            "uuid": UUID().uuidString,
            "timestamp": Int(Date().timeIntervalSince1970 * 1000),
        ]
        
        if let params = params, params.count > 0 {
            data["params"] = params
        }
        
        eventTrackingQueue.async { [weak self] in
            guard let strongSelf = self else {
                return
            }
            
            strongSelf.logs.append(data)
            strongSelf.saveLogs()
            
            strongSelf.upload()
        }
    }
    
    private func saveLogs() {
        guard let logsData = try? JSONSerialization
            .data(withJSONObject: logs, options: .prettyPrinted) else {
            return
        }
        
        ServiceManager.shared.cacheService.globalCache.setObject(value: logsData, key: "logsData")
    }
    
    private func removeUploadedLogs() {
        logs.removeFirst(uploadingLogsCount)
        setUploadLogs(count: 0)
        saveLogs()
    }
    
    private func setUploadLogs(count: Int) {
        uploadingLogsCount = count
        
        ServiceManager.shared.cacheService.globalCache.setInt(value: uploadingLogsCount, key: "uploadingLogsCount")
    }
    
    private func upload() {
        guard disposable == nil else {
            return
        }
        
        setUploadLogs(count: logs.count < EventTracking.maxUploadLogsCount ? logs.count : EventTracking.maxUploadLogsCount)
        
        disposable = EventTrackingService.shared
            .log(data: Array(logs.prefix(uploadingLogsCount)))
            .observe(on: eventTrackingSchedule)
            .subscribe(onSuccess: { [weak self] result in
                guard result.isSuccess() else {
                    self?.logger.error("\(result.message ?? "upload error")")
                    return
                }
                
                self?.removeUploadedLogs()
            }, onFailure: { [weak self] error in
                self?.logger.error("\(error.localizedDescription)")
            }, onDisposed: { [weak self] in
                guard let strongSelf = self else {
                    return
                }
                
                strongSelf.disposable = nil
                
                if strongSelf.uploadingLogsCount == 0, !strongSelf.logs.isEmpty {
                    strongSelf.upload()
                }
            })
    }
}
